package com.ljh.seckill.controller;

import com.ljh.seckill.config.SeckillConfig;
import com.ljh.seckill.entity.Resource;
import com.ljh.seckill.mq.Producer;
import com.ljh.seckill.service.OrderService;
import com.ljh.seckill.utils.LockFactory;
import com.ljh.seckill.utils.RedisLock;
import com.ljh.seckill.utils.SnowflakeIdWorker;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.locks.Lock;

/**
 * @Author grung
 * @Date 2021/2/23 10:28
 * @Version 1.0
 */
@Slf4j
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @Autowired
    private RedisLockRegistry redisLockRegistry;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;

    @Autowired
    private Producer producer;

    @Autowired
    private LockFactory lockFactory;



    /**
     * 创建订单
     * @param resourceId
     * @return
     */

    @PostMapping(value="create")
    public String createOrder(@RequestParam(name = "resourceId")String resourceId){

        //加锁，防止并发下单,库存计算错误
        Lock lock = redisLockRegistry.obtain(resourceId);

        try {
            lock.lock();

            //1.从缓存中查找资源库存，如果不存在就从数据库中加载并放入缓存中
            int stock;
            Object obj = stringRedisTemplate.opsForValue().get(SeckillConfig.RESOURCE_STOCK + resourceId);
            if (null==obj){

                Resource resource = orderService.findResourceById(resourceId);
                stock=resource.getStock();
                stringRedisTemplate.opsForValue().set(SeckillConfig.RESOURCE_STOCK+resourceId,stock+"");

            }else{
                stock=Integer.parseInt(obj.toString());
            }

            //2.检验库存，如果不足返回下单失败
            if (stock<0){
                return "fail";
            }

            //雪花算法生成订单ID
            String orderId = snowflakeIdWorker.nextId();

            //3. 扣减库存并发送一条订单创建消息
            Boolean result = producer.sendMsg(SeckillConfig.ROCKETMQ_TOPIC_ORDER,
                    SeckillConfig.ROCKETMQ_TAG_ORDER_CREATE,
                    orderId.concat(",").concat(resourceId));

            if (result){
                //扣减库存，直接在缓存扣减，后面消费了信息创建订单后从数据库减
                stringRedisTemplate.opsForValue().decrement(SeckillConfig.RESOURCE_STOCK+resourceId);
            }

        }catch (Exception e){
            log.error("系统异常，请稍后再试",e);
            return "fail";
        }finally {
            lock.unlock();
        }

        return "success";

    }

    /**
     * 创建订单，使用自己封装的分布式锁
     *
     * @param resourceId
     */
    @PostMapping(value = "create2")
    public String createOrder2(@RequestParam(name = "resourceId") String resourceId) {

        //加锁，防止并发下单，库存计算错误
        RedisLock lock = lockFactory.getLock(SeckillConfig.ORDER_LOCK);
        try {
            lock.lock(resourceId);
            //1. 从缓存中查找资源库存，如果不存在就从数据库中加载并放入缓存
            int stock;
            Object obj = stringRedisTemplate.opsForValue().get(SeckillConfig.RESOURCE_STOCK + resourceId);
            if (null == obj) {
                Resource resource = orderService.findResourceById(resourceId);
                stock = resource.getStock();
                stringRedisTemplate.opsForValue().set(SeckillConfig.RESOURCE_STOCK + resourceId, stock + "");
            } else {
                stock = Integer.parseInt(obj.toString());
            }

            //2. 校验库存，如果不足返回下单失败
            if (stock <= 0) {
                return "fail";
            }

            String orderId = snowflakeIdWorker.nextId();
            //3. 扣减库存并发送一条订单创建消息
            Boolean result = producer.sendMsg(SeckillConfig.ROCKETMQ_TOPIC_ORDER,
                    SeckillConfig.ROCKETMQ_TAG_ORDER_CREATE,
                    orderId.concat(",").concat(resourceId));

            if (result) {
                //扣减库存，直接在缓存扣减，后面消费消息时创建订单再从数据库扣减
                stringRedisTemplate.opsForValue().decrement(SeckillConfig.RESOURCE_STOCK + resourceId);
            }
        } catch (Exception e) {
            log.error("系统异常，请稍后重试", e);
            return "fail";
        } finally {
            lock.unlock(resourceId);
        }
        return "success";
    }


}
